home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 March: Reference Library / Dev.CD Mar 96 RL / Dev.CD Mar 96 RL.toast / Technical Documentation / develop / develop Issue 24 / develop Issue 24 code / Scriptable Database 1.0a11.sea / Scriptable Database 1.0a11 / Application / DBElementToken.cp / DBElementToken.cp
Encoding:
Text File  |  1995-10-18  |  18.9 KB  |  602 lines  |  [TEXT/MMCC]

  1.  
  2. #include "DBElementToken.h"
  3. #include "DBElementTokenIterator.h"
  4.  
  5. #include "Application.h"
  6. #include "DBElement.h"
  7. #include "DBProperty.h"
  8. #include "Transaction.h"
  9. #include "ScriptableDBDocument.h"
  10. #include "AbstractProperty.h"
  11.  
  12. #include <AERegistry.h>
  13. #include <ASRegistry.h>
  14.  
  15. #include "Exceptions.h"
  16.  
  17. TDescriptor GetDataAsDescriptor(TTransaction* t, AConst<TDBProperty> propertyRecord);
  18.  
  19.  
  20. //--------------------------------------------------------------------------------
  21. // GetDataAsDescriptor
  22. //--------------------------------------------------------------------------------
  23. TDescriptor GetDataAsDescriptor(TTransaction* t, AConst<TDBProperty> propertyRecord)
  24. {
  25.     TDescriptor result;
  26.     
  27.     if(propertyRecord.Exists())
  28.     {
  29.         //
  30.         // Bunches of code just to copy the property data into a handle
  31.         //
  32.         long propertyType = propertyRecord->GetDataType(t);
  33.         long propertyLength = propertyRecord->GetDataLength(t);
  34.         
  35.         Handle propertyDataStorage = NewHandle(propertyLength);
  36.         FailErr(MemError());
  37.         HLock(propertyDataStorage);
  38.         propertyRecord->GetTypedData(t, TUpdataDataReference(propertyType, *propertyDataStorage, propertyLength, propertyLength));
  39.         HUnlock(propertyDataStorage);
  40.         
  41.         //
  42.         // Once we have the property type and all of the data in
  43.         // a handle, we can package up a TDescriptor
  44.         //
  45.         result.AdoptHandle(propertyType, propertyDataStorage);
  46.     }
  47.     
  48.     return result;
  49. } // GetDataAsDescriptor
  50.  
  51. #define clDBElementToken 33
  52.  
  53. #pragma segment ObjectResident
  54. ImplementSmallClassData(TDBElementToken, clDBElementToken);
  55.  
  56. //--------------------------------------------------------------------------------
  57. // TDBElementToken::TDBElementToken
  58. //--------------------------------------------------------------------------------
  59. TDBElementToken::TDBElementToken(AConst<TDBElement> record) : fRecord(record)
  60.     {
  61.     record->AddReference();
  62.     }
  63.  
  64. //--------------------------------------------------------------------------------
  65. // TDBElementToken::~TDBElementToken
  66. //--------------------------------------------------------------------------------
  67. TDBElementToken::~TDBElementToken()
  68.     {
  69.     fRecord->RemoveReference();
  70.     }
  71.     
  72. //--------------------------------------------------------------------------------
  73. // TDBElementToken::CloneOwnedObjects
  74. //--------------------------------------------------------------------------------
  75. void TDBElementToken::CloneOwnedObjects()
  76.     {
  77.     //
  78.     // fRecord has been cloned, so we need to add one more reference to it
  79.     //    
  80.     fRecord->AddReference();
  81.     } // TDBElementToken::CloneOwnedObjects
  82.  
  83. //--------------------------------------------------------------------------------
  84. // TDBElementToken::ObjectClass
  85. //--------------------------------------------------------------------------------
  86. DescType TDBElementToken::ObjectClass(TTransaction* t, Boolean /*recordedClass*/)
  87. {
  88.     DescType objectClass = cObject;
  89.     long unusedLength = 0;
  90.     long unusedType = 0;
  91.     AConst<TDBProperty> propertyRecord = fRecord->GetPropertyRecord(t, pClass);
  92.     
  93.     if(propertyRecord.Exists())
  94.         propertyRecord->GetTypedData(t, TUpdataDataReference(typeType, (char*)&objectClass, sizeof(DescType), sizeof(DescType)));
  95.     
  96.     return objectClass;
  97. }
  98.  
  99. //--------------------------------------------------------------------------------
  100. // TDBElementToken::DerivedFromOSLClass
  101. //--------------------------------------------------------------------------------
  102. Boolean TDBElementToken::DerivedFromOSLClass(TTransaction* t, DescType objectClass)
  103. {
  104.     return ((objectClass == this->ObjectClass(t)) || (Inherited::DerivedFromOSLClass(t, objectClass)));
  105. }
  106.  
  107. //--------------------------------------------------------------------------------
  108. // TDBElementToken::ParentObject
  109. //--------------------------------------------------------------------------------
  110. TAbstractScriptableObject* TDBElementToken::ParentObject(TTransaction* t)
  111. {
  112.     TAbstractScriptableObject* parent = nil;
  113.     AConst<TDBRecord> parentElement = fRecord->TreeOwner(t);
  114.  
  115.     //
  116.     // If this node has a parent, then it is not the metaroot
  117.     //
  118.     if(parentElement.Exists())
  119.     {
  120.         parent = new TDBElementToken(parentElement->DBElementCursor());
  121.     }
  122.     //
  123.     // The parent of the metaroot is the scriptable db document
  124.     // that contains this database document.
  125.     //
  126.     else
  127.     {
  128.         TDatabaseDocument* doc = fRecord->DBDocument();
  129.         parent = gApplication->FindDocument(doc->ObjectsKeySpace());
  130.     }
  131.     
  132.     return parent;
  133. }
  134.  
  135. //--------------------------------------------------------------------------------
  136. // TDBElementToken::ElementIterator
  137. //--------------------------------------------------------------------------------
  138. TAbstractObjectIterator* TDBElementToken::ElementIterator(TTransaction* t)
  139. {    
  140.     // AConst<TDBRecord> elements = fRecord->Elements()->DBRecordCursor();
  141.     AConst<TDBElement> elements = fRecord->Elements(t);
  142.  
  143.     TAbstractObjectIterator* iter = nil;
  144.     
  145.     if(elements.Exists())
  146.         {
  147.         iter = new TDBElementTokenIterator(t, elements->DBRecordCursor());
  148.         }
  149.         
  150.     return iter;
  151. }
  152.  
  153.  
  154. //--------------------------------------------------------------------------------
  155. // TDBElementToken::AccessByProperty
  156. //--------------------------------------------------------------------------------
  157. TAbstractScriptableObject* TDBElementToken::AccessByProperty(TTransaction* t, DescType propertyIdentifier)
  158. {
  159.     TAbstractScriptableObject* result = nil;
  160.     
  161.     //
  162.     // If we can generate a description for this property,
  163.     // then call Inherited:: to make the property token
  164.     //
  165.     if(this->DescriptionOfProperty(t, propertyIdentifier) != nil)
  166.     {
  167.         result = Inherited::AccessByProperty(t, propertyIdentifier);
  168.     }
  169.     else
  170.     {
  171.         //
  172.         // Problem:  For this database, there should be some way
  173.         // to create a property that does not exist yet under some
  174.         // situations (maybe some databases will allow any property
  175.         // to be added to any element, but most databases will
  176.         // probably restrict the properties that each element can
  177.         // have).
  178.         //
  179.         // We also need to specify that some properties are read
  180.         // only (maybe?)
  181.         //
  182. #if 0
  183.         AConst<TDBProperty> propertyRecord = fRecord->GetPropertyRecord(propertyIdentifier);
  184.         if(propertyRecord.Exists())
  185. #endif
  186.         {
  187.             //
  188.             // We need some way to determine what the default type
  189.             // and best type for any given property should be.
  190.             //
  191.             result = new TGenericProperty;
  192.             ((TGenericProperty*)result)->IGenericProperty(this, TPropertyDataDescription(propertyIdentifier, 0, typeWildCard, typeWildCard));
  193.         }
  194.     }
  195.     
  196.     return result;
  197. }
  198.  
  199.  
  200. //================================================================================
  201. // Class TDescriptorComparisonObject
  202. //================================================================================
  203. class TDescriptorComparisonObject : public TAbstractDBComparisonObject
  204. {
  205. private:
  206.     const TDescriptor                fSearchKey;
  207.     
  208. public:
  209.                                     TDescriptorComparisonObject(TDescriptor& searchKey) : fSearchKey(searchKey) {};
  210.  
  211.     virtual CompareEnumeration        TestObject(TTransaction*, AConst<TDBRecord>);
  212. };
  213.  
  214. //--------------------------------------------------------------------------------
  215. // TDescriptorComparisonObject::TestObject
  216. //--------------------------------------------------------------------------------
  217. CompareEnumeration TDescriptorComparisonObject::TestObject(TTransaction* t, AConst<TDBRecord> testObject)
  218. {
  219.     //
  220.     // This is not a very fast way to compare, but it will do for now.
  221.     //
  222.     TDescriptor testName = GetDataAsDescriptor(t, testObject->DBElementCursor()->GetPropertyRecord(t, pName));
  223.  
  224.     //
  225.     // This AppleEvent API that removes information from the result is annoying,
  226.     // because it forces us to compare twice (ick).
  227.     //
  228.     // kAEGreaterThan
  229.     // kAELessThan
  230.     //
  231.     Boolean isLess = fSearchKey.Compare(kAELessThan, testName);
  232.     Boolean isGreater = fSearchKey.Compare(kAEGreaterThan, testName);
  233.     
  234.     //
  235.     // kSecondObjectComesBefore = -1,
  236.     // kObjectKeysEqual = 0,
  237.     // kSecondObjectComesAfter = 1
  238.     //
  239.     return (isGreater ? kSecondObjectComesAfter : (isLess ? kSecondObjectComesBefore : kObjectKeysEqual));
  240. } // TDescriptorComparisonObject::TestObject
  241.  
  242. //--------------------------------------------------------------------------------
  243. // TDBElementToken::AccessByUniqueID
  244. //--------------------------------------------------------------------------------
  245. TAbstractScriptableObject* TDBElementToken::AccessByUniqueID(TTransaction* t, DescType desiredClass, TDescriptor uniqueID)
  246. {
  247.     TAbstractScriptableObject* result = nil;
  248.     long theRecordIndex = uniqueID.GetLong();
  249.     
  250.     //
  251.     // Get the specified record from the database.  The record
  252.     // _must_ be a database element (not a property); if this is
  253.     // a token for the metaroot, then allow any record in any tree
  254.     // to be returned.  Otherwise, require that the record we found
  255.     // be a child of this record.
  256.     //    
  257.     AConst<TDBElement> theRecord = fRecord->DBDocument()->GetRecordCursor(theRecordIndex)->DBElementCursor();
  258.     if(theRecord.Exists())
  259.     {
  260.         AConst<TDBRecord> theRecordOwner = theRecord->TreeOwner(t);
  261.         if(this->IsMetaRoot() || (theRecordOwner->RecordIndex() == fRecord->RecordIndex()))
  262.         {
  263.             result = new TDBElementToken(theRecord);
  264.             if(result->DerivedFromOSLClass(t, desiredClass) == false)
  265.             {
  266.                 result->DisposeDesignator();
  267.                 result = nil;
  268.             }
  269.         }
  270.     }
  271.     
  272.     //
  273.     // If we could not find an appropriate record with the given ID,
  274.     // then call Inherited.
  275.     //
  276.     if(result == nil)
  277.         result = Inherited::AccessByUniqueID(t, desiredClass, uniqueID);
  278.         
  279.     return result;
  280. }
  281.  
  282. //--------------------------------------------------------------------------------
  283. // TDBElementToken::BestType
  284. //--------------------------------------------------------------------------------
  285. DescType TDBElementToken::BestType(TTransaction* t, DescType propertyName)
  286. {
  287.     AConst<TDBProperty> propertyRecord = fRecord->GetPropertyRecord(t, propertyName);
  288.     if(propertyRecord.Exists())
  289.     {
  290.         return propertyRecord->GetDataType(t);
  291.     }
  292.     else
  293.         return typeNull;
  294. }
  295.  
  296. //--------------------------------------------------------------------------------
  297. // TDBElementToken::DefaultType
  298. //--------------------------------------------------------------------------------
  299. DescType TDBElementToken::DefaultType(TTransaction* t, DescType propertyName)
  300. {
  301.     AConst<TDBProperty> propertyRecord = fRecord->GetPropertyRecord(t, propertyName);
  302.     if(propertyRecord.Exists())
  303.     {
  304.         return propertyRecord->GetDataType(t);
  305.     }
  306.     else
  307.         return typeNull;
  308. }
  309.  
  310. //--------------------------------------------------------------------------------
  311. // TDBElementToken::CanReturnDataOfType
  312. //--------------------------------------------------------------------------------
  313. Boolean TDBElementToken::CanReturnDataOfType(TTransaction*, DescType /*propertyName*/, DescType /*desiredType*/)
  314. {
  315.     return true;
  316. }
  317.  
  318.  
  319. //--------------------------------------------------------------------------------
  320. // TDBElementToken::GetProperty
  321. //--------------------------------------------------------------------------------
  322. TDescriptor TDBElementToken::GetProperty(TTransaction* t, DescType propertyName, DescType desiredType, unsigned long additionalInfo /*= 0*/)
  323. {
  324.     TDescriptor result;
  325.     
  326.     switch(propertyName)
  327.     {
  328.         case pID:
  329.         {
  330.             result.MakeLong(fRecord->RecordIndex());
  331.             break;
  332.         }
  333.         
  334.         case pIndex:
  335.         {
  336.             if(this->IsMetaRoot())
  337.                 FailErr(errAENoSuchObject);
  338.             else
  339.             {
  340.                 long index = 0;
  341.                 AConst<TDBRecord> treeTop = fRecord->TopOfTree(t);
  342.                 for(TAbstractRecordIterator iter(t, treeTop); iter.More(); iter.Next())
  343.                 {
  344.                     ++index;
  345.                     if(iter.Current()->RecordIndex() == fRecord->RecordIndex())
  346.                         break;
  347.                 }
  348.                 result.MakeLong(index);
  349.             }
  350.             break;
  351.         }
  352.         
  353.         default:
  354.         {
  355.             AConst<TDBProperty> propertyRecord = fRecord->GetPropertyRecord(t, propertyName);
  356.             if(propertyRecord.Exists())
  357.             {
  358.                 result = GetDataAsDescriptor(t, propertyRecord);
  359.             }
  360.             else
  361.                 result = Inherited::GetProperty(t, propertyName, desiredType, additionalInfo);
  362.             
  363.             break;
  364.         }
  365.     }
  366.     
  367.     return result;
  368. }
  369.  
  370. //--------------------------------------------------------------------------------
  371. // TDBElementToken::CompareProperty
  372. //--------------------------------------------------------------------------------
  373. Boolean TDBElementToken::CompareProperty(TTransaction* t, DescType propertyIdentifier, DescType comparisonOperator, TDescriptor compareWith)
  374. {
  375.     Boolean compareResult = false;
  376.     Boolean didCompare = false;
  377.     
  378.     AConst<TDBProperty> propertyRecord = fRecord->GetPropertyRecord(t, propertyIdentifier);
  379.     if(propertyRecord.Exists())
  380.     {
  381.         short oldState = compareWith.Lock();
  382.  
  383.         switch(comparisonOperator)
  384.         {
  385.             case kAEEquals:        
  386.             case kASNotEqual:
  387.             case kAEGreaterThan:
  388.             case kAEGreaterThanEquals:
  389.             case kAELessThan:
  390.             case kASLessThanOrEqual:
  391.             {
  392.                 compareResult = InterpretCompareResult(comparisonOperator, propertyRecord->Compare(t, compareWith), 0 );
  393.                 didCompare = true;
  394.                 break;
  395.             }
  396.             
  397.             case kAEContains:
  398.             {
  399.                 compareResult = propertyRecord->Contains(t, compareWith);
  400.                 didCompare = true;
  401.                 break;
  402.             }
  403.             
  404.             //
  405.             // We don't export enough data to do begins with or
  406.             // ends with, so we'll just call Inherited and allow
  407.             // the comparison to be done the slow way.
  408.             //
  409.             case kAEBeginsWith:
  410.             case kAEEndsWith:
  411.             default:
  412.             {
  413.                 didCompare = false;
  414.                 break;
  415.             }
  416.         }
  417.  
  418.         compareWith.Unlock(oldState);
  419.     }
  420.     else
  421.         didCompare = false;
  422.  
  423.     //
  424.     // If we couldn't do the compare, then call Inherited
  425.     //
  426.     if(didCompare == false)
  427.         compareResult = Inherited::CompareProperty(t, propertyIdentifier, comparisonOperator, compareWith);
  428.     
  429.     return compareResult;
  430. } // TDBElementToken::CompareProperty
  431.  
  432. //--------------------------------------------------------------------------------
  433. // TDBElementToken::SetProperty
  434. //--------------------------------------------------------------------------------
  435. void TDBElementToken::SetProperty(TTransaction* transaction, DescType propertyName, TDescriptor& data, unsigned long /*additionalInfo = 0*/)
  436. {
  437.     switch(propertyName)
  438.     {
  439.         case pIndex:
  440.         case pID:
  441.         {
  442.             FailErr(errAENotModifiable);
  443.             break;
  444.         }
  445.         
  446.         default:
  447.         {
  448.             AnUpdate<TDBElement> updateElement = transaction->GetDBElementUpdatePointer(fRecord);
  449.         
  450.             HLock(data.DataHandle());
  451.             char* dataPtr = *data.DataHandle();
  452.             long dataLength = GetHandleSize(data.DataHandle());
  453.             long dataType = data.DescriptorType();
  454.             
  455.             updateElement->AddTypedProperty(transaction, propertyName, TConstDataReference(dataType, dataPtr, dataLength));
  456.             HUnlock(data.DataHandle());
  457.             
  458.             //
  459.             // Make sure that the tree is still happy
  460.             //
  461.             fRecord->Verify(transaction, true);
  462.             break;
  463.         }
  464.     }
  465. }
  466.  
  467. //--------------------------------------------------------------------------------
  468. // TDBElementToken::BuildObjectSpecifier
  469. //--------------------------------------------------------------------------------
  470. TDescriptor TDBElementToken::BuildObjectSpecifier(TTransaction* t)
  471. {
  472.     //
  473.     // If this record is the metaroot, don't build
  474.     // an object specifier for it; just make one
  475.     // for the parent object
  476.     //
  477.     if(this->IsMetaRoot())
  478.         return this->BuildSpecifierForParent(t);
  479.     else
  480.         return Inherited::BuildObjectSpecifier(t);
  481. }
  482.  
  483. //--------------------------------------------------------------------------------
  484. // TDBElementToken::MakeKeyDataForSelf
  485. //--------------------------------------------------------------------------------
  486. void TDBElementToken::MakeKeyDataForSelf(TTransaction* t, DescType& keyForm, TDescriptor& keyData)
  487. {
  488.     //
  489.     // If we have a name property and this record is in a tree,
  490.     // then describe this element as "element xxx" of <parent specifier>
  491.     //
  492.     AConst<TDBProperty> nameProperty = fRecord->GetPropertyRecord(t, pName);
  493.     if(nameProperty.Exists() && fRecord->RecordIsInATree(t))
  494.     {
  495.         Inherited::MakeKeyDataForSelf(t, keyForm, keyData);
  496.     }
  497.     //
  498.     // If there is no name property, OR if we have no parent, then describe
  499.     // this element by unique ID.
  500.     //
  501.     else
  502.     {
  503.         keyForm = formUniqueID;
  504.         keyData.MakeLong(fRecord->RecordIndex());
  505.     }
  506. }
  507.  
  508. //--------------------------------------------------------------------------------
  509. // TDBElementToken::BuildSpecifierForParent
  510. //--------------------------------------------------------------------------------
  511. TDescriptor TDBElementToken::BuildSpecifierForParent(TTransaction* t)
  512. {
  513.     return Inherited::BuildSpecifierForParent(t);
  514. }
  515.  
  516. //--------------------------------------------------------------------------------
  517. // TDBElementToken::AECommand
  518. //--------------------------------------------------------------------------------
  519. TDescriptor TDBElementToken::AECommand(TTransaction* t, TAEvent ae, TAEvent reply, long aeCommandID, TAbstractScriptableObject* auxObjects, long auxInfo)
  520. {
  521.     TDescriptor result;
  522.     
  523.     switch(aeCommandID)
  524.     {
  525.         case kAEDelete:
  526.         {
  527.             AnUpdate<TDBElement> updateElement = t->GetDBElementUpdatePointer(fRecord);
  528.             updateElement->FreeThisRecord(t);
  529.             
  530.             break;
  531.         }
  532.         
  533.         default:
  534.         {
  535.             result = Inherited::AECommand(t, ae, reply, aeCommandID, auxObjects, auxInfo);
  536.             break;
  537.         }
  538.     }
  539.     
  540.     return result;
  541. }
  542.  
  543. //--------------------------------------------------------------------------------
  544. // TDBElementToken::CreateNewElement
  545. //--------------------------------------------------------------------------------
  546. TAbstractScriptableObject* TDBElementToken::CreateNewElement(TTransaction* transaction, TAEvent /*ae*/, TAEvent /*reply*/, DescType newObjectClass, TDescriptor /*initialData*/, TDescriptor /*initialProperties*/, Boolean& /*usedInitialData*/, Boolean& /*usedInitialProperties*/)
  547. {
  548.     AnUpdate<TDBElement> updateElement = transaction->GetDBElementUpdatePointer(fRecord);
  549.     
  550.     AnUpdate<TDBElement> newElement = fRecord->DBDocument()->NewDBElement(transaction);
  551.     newElement->AddTypedProperty(transaction, pClass, TConstDataReference(typeType, (Ptr)&newObjectClass, sizeof(DescType)));
  552.     updateElement->AddElement(transaction, newElement);
  553.  
  554.     //
  555.     // Make sure that the tree is still happy
  556.     //
  557.     fRecord->Verify(transaction, true);
  558.  
  559.     return new TDBElementToken(newElement);
  560. }
  561.  
  562. //----------------------------------------------------------------------------------------
  563. // TDBElementToken::ObjectsAreTheSame: 
  564. //----------------------------------------------------------------------------------------
  565. Boolean TDBElementToken::ObjectsAreTheSame(TTransaction*, TAbstractScriptableObject* objectToTest)
  566. {
  567.     Boolean areTheSame = false;
  568.     
  569.     if(objectToTest->DerivedFrom(clDBElementToken))
  570.     {
  571.         areTheSame = ((this->RecordIndex() == ((TDBElementToken*)objectToTest)->RecordIndex()) &&
  572.                 (this->DocumentIdentifier() == ((TDBElementToken*)objectToTest)->DocumentIdentifier()));
  573.     }
  574.         
  575.     return areTheSame;
  576. } // TDBElementToken::ObjectsAreTheSame
  577.  
  578. //--------------------------------------------------------------------------------
  579. // TDBElementToken::IsMetaRoot
  580. //--------------------------------------------------------------------------------
  581. Boolean TDBElementToken::IsMetaRoot()
  582. {
  583.     return fRecord->RecordIndex() == fRecord->DBDocument()->MetaRootIndex();
  584. } // TDBElementToken::IsMetaRoot
  585.  
  586. //--------------------------------------------------------------------------------
  587. // TDBElementToken::RecordIndex
  588. //--------------------------------------------------------------------------------
  589. long TDBElementToken::RecordIndex()
  590. {
  591.     return fRecord->RecordIndex();
  592. } // TDBElementToken::RecordIndex
  593.  
  594. //--------------------------------------------------------------------------------
  595. // TDBElementToken::DocumentIdentifier
  596. //--------------------------------------------------------------------------------
  597. Int64 TDBElementToken::DocumentIdentifier()
  598. {
  599.     return fRecord->DBDocument()->ObjectsKeySpace();
  600. } // TDBElementToken::DocumentIdentifier
  601.  
  602.